package org.xiaom.butler.util;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.transaction.CMTTransactionFactory;

/**
 * Hibernate 工具类 用于初始化Hibernate,并进行Session和Transaction的管理
 */
public class HibernateUtil {

        private static Log log = LogFactory.getLog(HibernateUtil.class);

        private static final String INTERCEPTOR_CLASS = "hibernate.util.interceptor_class";

        private static Configuration configuration;

        private static SessionFactory sessionFactory;

        private static boolean useThreadLocal = true;

        // 保存Session对象实例的线程局部变量
        private static ThreadLocal<Session> threadSession = new ThreadLocal<Session>();

        // 保存Transaction对象实例的线程局部变量
        private static ThreadLocal<Transaction> threadTransaction = new ThreadLocal<Transaction>();

        static {
                try {
                        // 创建Configuration对象
                        configuration = new Configuration();

                        // 读取hibernate.properties或者hibernate.cfg.xml文件
                        configuration.configure();

                        // 指定一个全局的用户子定义的拦截器
                        String interceptorName = configuration.getProperty(INTERCEPTOR_CLASS);
                        if (interceptorName != null) {
                                Class<?> interceptorClass = HibernateUtil.class.getClassLoader().loadClass(interceptorName);
                                Interceptor interceptor = (Interceptor) interceptorClass.newInstance();
                                configuration.setInterceptor(interceptor);
                        }

                        // 如果使用CMT,那么就不使用线程安全的Session和Transaction
                        if (CMTTransactionFactory.class.getName().equals(configuration.getProperty(Environment.TRANSACTION_STRATEGY))) {
                                useThreadLocal = false;
                        }

                        if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
                                // 绑定Hibernate到JNDI
                                configuration.buildSessionFactory();
                        } else {
                                // 使用静态的变量
                                sessionFactory = configuration.buildSessionFactory();
                        }
                } catch (Throwable ex) {
                        // 必须捕获Throwable,否则不能捕获NoClassDefFoundError异常以及它的子类错误
                        log.error("Building SessionFactory failed!", ex);
                        throw new ExceptionInInitializerError(ex);
                }
        }

        /**
         * 返回全局的SessionFactory对象的实例
         * 
         * @return SessionFactory
         */
        public static SessionFactory getSessionFactory() {
                SessionFactory sf = null;
                String sfName = configuration.getProperty(Environment.SESSION_FACTORY_NAME);
                if (sfName != null) {
                        log.debug("Looking up SessionFactory in JNDI.");
                        try {
                                sf = (SessionFactory) new InitialContext().lookup(sfName);
                        } catch (NamingException e) {
                                throw new RuntimeException(e);
                        }
                } else {
                        sf = sessionFactory;
                }
                if (sf == null) {
                        throw new IllegalStateException("SessionFactory not available.");
                }
                return sf;
        }

        /**
         * 重新构建SessionFactory对象的实例
         * 
         */
        public static void rebuildSessionFactory() {
                log.debug("Using current Configuration for rebuild");
                rebuildSessionFactory(configuration);
        }

        /**
         * 使用指定的Configuration对象重新构建SessionFactory对象的实例
         * 
         * @param cfg
         */
        public static void rebuildSessionFactory(Configuration cfg) {
                log.debug("Rebuilding the SessionFactory from given Configuration.");
                synchronized (sessionFactory) {
                        if (sessionFactory != null && !sessionFactory.isClosed()) {
                                sessionFactory.close();
                        }
                        if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
                                cfg.buildSessionFactory();
                        } else {
                                sessionFactory = cfg.buildSessionFactory();
                        }
                        configuration = cfg;
                }
        }

        /**
         * 关闭当前SessionFactory并且释放所有资源
         * 
         */
        public static void shutdown() {
                log.debug("Shutting down Hibernate.");
                // 关闭缓冲和连接池
                getSessionFactory().close();

                // 清除静态变量
                configuration = null;
                sessionFactory = null;

                // 清除本地进程变量
                threadSession.set(null);
                threadTransaction.set(null);
        }

        /**
         * 获得当前Session对象的实例
         * 
         * @return Session
         */
        public static Session getCurrentSession() {
                if (useThreadLocal) {
                        Session s = (Session) threadSession.get();
                        if (s == null) {
                                log.debug("Opening new Session for this thread.");
                                s = getSessionFactory().openSession();
                                threadSession.set(s);
                                s.setFlushMode(FlushMode.AUTO);
                        }
                        return s;
                } else {
                        return getSessionFactory().getCurrentSession();
                }
        }

        /**
         * 重新连接当前的Session
         * 
         * @param session
         */
        @SuppressWarnings("deprecation")
        public static void reconnect(Session session) {
                if (useThreadLocal) {
                        log.debug("Reconnecting Session to this thrwad.");
                        session.reconnect();
                        threadSession.set(session);
                } else {
                        log.error("Using CMT/JTA,intercepted not supported reconnect call.");
                }
        }

        /**
         * 断开当前Session
         * 
         * @return Session the disconnected Session
         */
        public static Session disconnectedSession() {
                if (useThreadLocal) {
                        Transaction tx = (Transaction) threadTransaction.get();
                        if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack())) {
                                throw new IllegalStateException("Disconnecting Session but Transaction still open.");
                        }
                        Session session = getCurrentSession();
                        threadSession.set(null);
                        if (session.isConnected() && session.isOpen()) {
                                log.debug("Disconnecting Session from this thread.");
                                session.disconnect();
                        }
                        return session;
                } else {
                        log.error("Using CMT/JTA,intercepted not supported disconnect call.");
                        return null;
                }
        }

        /**
         * 关闭Session对象
         * 
         */
        public static void closeSession() {
                if (useThreadLocal) {
                        Session s = (Session) threadSession.get();
                        threadSession.set(null);
                        Transaction tx = (Transaction) threadTransaction.get();
                        if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack())) {
                                throw new IllegalStateException("Closing Session but Transaction still open.");
                        }
                        if (s != null && s.isOpen()) {
                                log.debug("Closing Session of this thread.");
                                s.close();
                        }
                } else {
                        log.warn("Using CMT/JTA,intercepted superfluous close call.");
                }
        }

        /**
         * 开始一个新的数据库事务
         * 
         */
        public static void beginTransaction() {
                if (useThreadLocal) {
                        Transaction tx = (Transaction) threadTransaction.get();
                        if (tx == null) {
                                log.debug("Starting new database transaction in this thread.");
                                tx = getCurrentSession().beginTransaction();
                                threadTransaction.set(tx);
                        }

                } else {
                        log.warn("Using CMT/JTA,intercepted superfluous tx begin call.");
                }
        }

        /**
         * 提交数据库事务
         * 
         */
        public static void commitTransaction() {
                if (useThreadLocal) {
                        try {
                                Transaction tx = (Transaction) threadTransaction.get();
                                if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
                                        log.debug("Committing database transaction of this thread.");
                                        tx.commit();
                                }
                                threadTransaction.set(null);
                        } catch (RuntimeException e) {
                                log.error(e);
                                rollbackTransaction();
                                throw e;
                        }
                } else {
                        log.warn("Using CMT/JTA,intercepted superfluous tx commit call.");
                }
        }

        /**
         * 回滚数据库事务
         * 
         */
        public static void rollbackTransaction() {
                if (useThreadLocal) {

                        Transaction tx = (Transaction) threadTransaction.get();
                        try {
                                threadTransaction.set(null);
                                if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
                                        log.debug("Trying to rollback database transaction of this thread.");
                                        tx.rollback();
                                }
                        } catch (RuntimeException e) {
                                throw new RuntimeException("Migth swallow original cause,check Error log!", e);
                        } finally {
                                closeSession();
                        }
                } else {
                        log.warn("Using CMT/JTA,intercepted superfluous tx rollback call.");
                }
        }
}